home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 2
/
Aminet AMIGA CDROM (1994)(Walnut Creek)[Feb 1994][W.O. 44790-1].iso
/
Aminet
/
misc
/
amag
/
sh9301c.lha
/
Differenzieren(S.91)
/
listing.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-10-24
|
9KB
|
459 lines
/* Differenzieren eines mathematischen Ausdruckes */
/* von Markus Öllinger */
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
typedef enum {OP_OPEN=-2,OP_CLOSE,OP_NONE=0,OP_ADD,OP_SUB,
OP_MUL,OP_DIV,OP_NEG,OP_POT,OP_FUNC1} OP;
struct Knoten {
struct Knoten *l,*r;
OP Operator;
short int Pri;
short int Len;
char *Term;
};
struct Knoten *Neuer_Operand(char *cp,char **x);
void verwende_Operator(OP);
struct Knoten *Differenziere(struct Knoten *);
extern char *Funktion[];
OP operator_stack[1024]; /* Der Operator-Stapel */
int sp_operator; /* Sein Stapelzeiger */
struct Knoten *operand_stack[1024];
int sp_operand;
/* wandle Operatorenzeichen in Token um */
OP Operator(int op)
{
switch(op) {
case '+': return OP_ADD;
case '-': return OP_SUB;
case '*': return OP_MUL;
case '/': return OP_DIV;
case '_': return OP_NEG;
case '^': return OP_POT;
}
return 0;
}
/* bestimme Priorität des Operators */
Pri(OP op)
{
switch(op) {
case OP_ADD:
case OP_SUB: return 1;
case OP_MUL:
case OP_DIV: return 2;
case OP_NEG: return 3;
case OP_POT: return 4;
case OP_NONE: return 0;
default: return 5;
}
}
/* konstruiere Baum aus String */
struct Knoten *Baum_aufbauen(char *term)
{
char *cp=term;
int i,l,pri,p;
OP op,op2;
char c;
while((c=*cp)!=0) {
if(c=='(') operator_stack[sp_operator++]=OP_OPEN;
else if(isalnum(c)) {
if(isalpha(c)) {
for(i=0;Funktion[i];i++)
if(!strncmp(Funktion[i],cp,l=strlen(Funktion[i]))) {
pri=5;
op=OP_FUNC1+i;
cp+=l-1;
goto operator;
}
}
operand_stack[sp_operand++]=Neuer_Operand(cp,&cp);
cp--;
}
else if(c==')')
for(;;) {
if(!sp_operator) goto error;
op=operator_stack[--sp_operator];
if(op!=OP_OPEN) verwende_Operator(op);
else break;
}
else {
op=Operator(c);
pri=Pri(op);
/* Subtraktion oder Negation? */
if(op==OP_SUB && (cp==term || cp[-1]=='(')) {
op=OP_NEG;
pri=3;
}
operator: /* verwende alle Operatoren höherer Priorität */
while(sp_operator) {
op2=operator_stack[sp_operator-1];
p=Pri(op2);
/* Prioritätsvergleich. Achtung ^ ist rechtsassoziativ */
if(op2==OP_OPEN || p<pri || p==pri && p==4)
break;
--sp_operator;
verwende_Operator(op2);
}
operator_stack[sp_operator++]=op;
}
cp++;
}
while(sp_operator) {
op2=operator_stack[--sp_operator];
if(op2==OP_CLOSE) goto error;
verwende_Operator(op2);
}
if(sp_operand!=1) goto error;
return operand_stack[--sp_operand];
error:
return NULL;
}
/* gibt Baum wieder frei (rekursiv, wie alles) */
void Baum_abbauen(struct Knoten *k)
{
if(k) {
if(k->l) Baum_abbauen(k->l);
if(k->r) Baum_abbauen(k->r);
free(k);
}
}
/* Hilfsroutinen */
struct Knoten *Neuer_Knoten(void)
{
struct Knoten *k=malloc(sizeof(struct Knoten));
if(k==NULL) {
fprintf(stderr,"Speicher voll!\n");
exit(10);
}
memset((void *)k,0,sizeof(*k));
return k;
}
struct Knoten *Neuer_Operand(char *cp,char **x)
{
struct Knoten *k;
char *s;
k=Neuer_Knoten();
s=cp;
while(*++s=='.' || isalnum(*s));
k->Term=cp;
k->Len=s-cp;
*x=s;
return k;
}
char *Op(OP op)
{
static char c[]={" +-*/-^"};
if(op>=OP_FUNC1) return Funktion[op-OP_FUNC1];
return &c[op];
}
struct Knoten *Neuer_Operator(OP op)
{
struct Knoten *k;
k=Neuer_Knoten();
k->Operator=op;
k->Pri=Pri(op);
k->Term=Op(op);
k->Len=op>=OP_FUNC1?strlen(k->Term):1;
return k;
}
/* holt ein od. zwei Operanden vom Operandenstapel
* und verknüpft sie mit dem angeg. Operator und
* legt Ergebnis wieder zurück.
*/
void verwende_Operator(OP op)
{
struct Knoten *k;
int p=Pri(op);
k=Neuer_Operator(op);
k->r=operand_stack[--sp_operand];
switch(p) {
case 1:
case 2:
case 4:
k->l=operand_stack[--sp_operand];
k->Operator=op;
k->Pri=p;
break;
}
operand_stack[sp_operand++]=k;
}
/* erzeugt eine Kopie des Baumes */
struct Knoten *BaumKopie(struct Knoten *k)
{
struct Knoten *n=NULL;
if(k) {
n=Neuer_Operator(OP_NONE);
*n=*k;
n->l=BaumKopie(k->l);
n->r=BaumKopie(k->r);
}
return n;
}
/* Geht die in "Regel" gespeicherte Differenzierregel durch.
* Diese ist in Präfixform gespeichert und hat folgende Syntax:
* u entspricht dem linken Operanden (nicht differenziert)
* a entspricht dem linken Operanden (aber differenziert)
* v und b dasselbe rechts
* + - * / ^ entsprechen den jeweilgen Operatoren.
* _ entspricht dem (unären) Negationsoperator (Minus-Vorzeichen)
* #XX entspricht einer Funktion, XX gibt die Funktionsnummer an.
* #01 ist die Funktion mit dem im Feldelement Funktion[0]
* gespeicherten Namen (hier ln), #02 entspr. Funktion[1] usw.
* Eine Ziffer steht für die angegebene Zahl.
*
* in den globalen Variablen u, v, a und b werden die
* gleichnamigen Teilbäume erwartet.
*/
char *Regel; /*zu verwendende Regel*/
struct Knoten *u,*v,*a,*b;
/* Hier sind sie: die Differenzierregeln für
* + - * / Negation und ^.
*/
char *dRegeln[]={
NULL,"+ab","-ab","+*av*bu","/-*av*bu^v2",
"_b","*^uv+*b#01u*/vua"};
/* Die Funktionsnamen und die Regeln für die Ableitung */
char *Funktion[]={"ln","sinh","cosh","sin","cos",
"exp",NULL};
char *dFunk[]={"/bv","*#03vb","*#02vb","*#05vb","*_#04vb",
"*#06vb"};
struct Knoten *Regeln(void)
{
struct Knoten *k;
int d;
switch(d=*Regel++) {
case 'a': k=BaumKopie(a);break;
case 'b': k=BaumKopie(b);break;
case 'u': k=BaumKopie(u);break;
case 'v': k=BaumKopie(v);break;
case '_': k=Neuer_Operator(OP_NEG);k->r=Regeln();break;
case '#': /* Funktion */
sscanf(Regel,"%d",&d);
Regel+=2;
k=Neuer_Operator(d+OP_FUNC1-1);
k->r=Regeln();
break;
default:
if(isdigit(d)) { /* Zahl */
k=Neuer_Knoten();
k->Term=Regel-1;
k->Len=1;
} else { /* Operator */
k=Neuer_Operator(Operator(d));
k->l=Regeln();
k->r=Regeln();
}
}
return k;
}
/* erledigt das Differenzieren mittels Rekursion */
struct Knoten *Differenziere(struct Knoten *k)
{
static char One='1',Zero='0';
struct Knoten *d;
if(k->Operator==OP_NONE) { /* Blatt (Operand) */
d=Neuer_Knoten();
if(k->Len==1 && k->Term[0]=='x')
d->Term=&One;
else d->Term=&Zero;
d->Len=1;
return d;
}
if(k->Operator>=OP_FUNC1) { /* Funktion */
b=Differenziere(k->r);
v=k->r;
Regel=dFunk[k->Operator-OP_FUNC1];
d=Regeln();
Baum_abbauen(b);
return d;
}
/* Operator */
if(k->l) d=Differenziere(k->l);
else d=NULL;
b=Differenziere(k->r);
a=d;
u=BaumKopie(k->l);
v=BaumKopie(k->r);
Regel=dRegeln[k->Operator];
d=Regeln();
Baum_abbauen(u);
Baum_abbauen(v);
Baum_abbauen(a);
Baum_abbauen(b);
return d;
}
/* Baum zurück in String umwandeln */
/* hat der Vater v einen Sohn s mit einem Operator
* niedrigerer Priorität?
*/
pri_klammer(struct Knoten *v,struct Knoten *s,char **sp)
{
if(v->Pri>s->Pri && s->Pri!=0 || v->Pri==s->Pri && v->Pri==4) {
(*sp)[0]='(';
(*sp)++;
return 1;
}
return 0;
}
/* mach ev. Klammer wieder zu */
void klammer_zu(char **sp,int kl)
{
if(kl) {
(*sp)[0]=')';
(*sp)++;
}
}
void Baum_zu_String(struct Knoten *k,char *s)
{
extern char *Baum_String(struct Knoten *,char *);
*Baum_String(k,s)=0;
}
char *Baum_String(struct Knoten *k,char *s)
{
int kl;
if(k->Operator==OP_NONE) {
strncpy(s,k->Term,k->Len);
s+=k->Len;
return s;
}
if(k->Operator>=OP_FUNC1) {
strncpy(s,k->Term,k->Len);
s+=k->Len;
kl=pri_klammer(k,k->r,&s);
if(!kl) *s++=' ';
s=Baum_String(k->r,s);
klammer_zu(&s,kl);
return s;
}
if(k->Operator==OP_NEG) {
*s++='-';
kl=pri_klammer(k,k->r,&s);
s=Baum_String(k->r,s);
klammer_zu(&s,kl);
return s;
}
kl=pri_klammer(k,k->l,&s);
if(!kl && k->Operator==OP_POT && k->l->Operator==OP_POT) {
*s++='(';
kl=1;
}
s=Baum_String(k->l,s);
klammer_zu(&s,kl);
*s++=*Op(k->Operator);
kl=pri_klammer(k,k->r,&s);
if(!kl && (k->Operator==OP_SUB || k->Operator==OP_DIV) && k->r->Pri==k->Pri) {
*s++='(';
kl=1;
}
s=Baum_String(k->r,s);
klammer_zu(&s,kl);
return s;
}
/*** Einsprung zum Differenzieren
*** (Term muß korrekt sein)
***/
void Diff(char *Angabe,char *Ergebnis)
{
struct Knoten *Root,*d;
Root=Baum_aufbauen(Angabe);
Baum_zu_String(Root,Ergebnis);
puts(Ergebnis);
d=Differenziere(Root);
Baum_zu_String(d,Ergebnis);
Baum_abbauen(d);
Baum_abbauen(Root);
}
/* nur ein kurzer Check */
Term_korrekt(char *term)
{
char c,*lastop=NULL,*p=term,*cp=term-1;
int kl=0;
while((c=*++cp)!=0) if(!isspace(c)) {
if(strchr("()+-*/^",c)) {
if(c=='(') kl++;
else if(c==')') {
if(--kl<0 || lastop+1==cp || cp[-1]=='(') return 0;
}
else {
if(lastop+1==cp || c!='-' && cp[-1]=='(') return 0;
lastop=cp;
}
}
else if(!isalnum(c) && c!='.') return 0;
*p++=c;
}
*p=0;
return kl==0;
}
char Angabe[1024],Ergebnis[1024];
void main(int argc,char **argv)
{
if(argc==2 && Term_korrekt(strcpy(Angabe,argv[1]))) {
Diff(Angabe,Ergebnis);
puts(Ergebnis);
}
}